#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#* GNU General Public License for more details.
#*
#* You should have received a copy of the GNU General Public License v2
#* along with this program; if not, write to the Free Software
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#*
#* @author               Zambon Andrea <zamby82@alice.it>
#*/

import sys
import os
import os.path
import string
import time
import sqlite3
import subprocess
import signal

print "Stopppy.py 1.1 - GPL2 - zamby.ing   e t   gmail.com - WITHOUT ANY WARRANTY; "

def writeAlert(mes,pr):
#  print "Stopppy: " + mes;
  pf = file("/var/log/stopppy.log",'a')
  print >> pf,getNow(),str(mes)
  pf.close() 
                    
  

def checkLimit():            
    if (checkDayB()==True):
            writeAlert("Day limit out",0)
            stopConnessione()
    if (checkMonB()==True):
            writeAlert("Month limit out",0)
            stopConnessione()
    if (checkSinB()==True):
            writeAlert("Single limit out",0)
            stopConnessione()


def checkDayB():
  global c,bDayMax
  if (bDayMax==None): return False
  tot = totDayB()
  writeAlert("Check    day:" + str(tot) + " >= " + str(bDayMax) + " - " + str( bStop),2)
  if ( tot >= bDayMax - bStop ): 
     return True
  return False


def checkMonB():
  global c,bMonMax,bStop
  if (bMonMax==None): return False
  tot = totMonB()
  writeAlert("Check  month:" + str(tot) + " >= " + str(bMonMax) + " - " + str( bStop),2)
  if ( tot >= bMonMax - bStop ): 
    return True
  return False

def checkSinB():
  global c, bSinMax,bStop
  if (bSinMax==None): return False 
  max = totMaxS()
  writeAlert("Check single:" + str(max) + " >= " + str(bSinMax) + " - " + str( bStop),2)
  if ( max >= bSinMax - bStop ): 
      return True
  return False


def totMaxS():
  c.execute("SELECT id,Download,Upload FROM Log WHERE gdI='" + str(now[3]) + "' AND mdI='" + str(now[4]) + "' AND adI='" + str(now[5]) + "'  AND  eseguita=0;")
  tot=0
  max=0
  for row in c:
      tot=row[1]+row[2]
      if (tot>max):
         max=tot
  return max

def totDayB():
  global c,now
  sql="SELECT id,Download,Upload FROM Log WHERE gdI='"+str(now[3])+"' AND mdI='"+str(now[4])+"' AND adI='"+str(now[5])+"' AND (eseguita=0 OR eseguita=1);"
  c.execute(sql)
  tot=0
  for row in c:
    tot=tot+row[1]+row[2]
  return tot


def statistics():
  global bDayMax,bMonMax,bStop
  tdb=totDayB()
  tmb=totMonB()
  tsb=totMaxS()
  if (bDayMax==None or bDayMax==0):
    pdb="--"
  else:
    pdb=int((tdb/(bDayMax-bStop))*100.0)
  if (bMonMax==None or bMonMax==0):
    pmb="--"
  else:
    pmb=int((tmb/(bMonMax-bStop))*100.0)
  if (bSinMax==None or bSinMax==0):
    psb="--"
  else:
    psb=int((tsb/bSinMax)*100.0)
  print "Day   : " + str(pdb) + "% " + str(tdb)       + " / " + str(bDayMax) 
  print "Month : " + str(pmb) + "% " + str(tmb)       + " / " + str(bMonMax)
  print "Single: " + str(psb) + "% " + str(totMaxS()) + " / " + str(bSinMax)


def listConn():
  global c,bDayMax,bMonMax
  c.execute("SELECT * from Log;")
  for row in c:
    print row



def totMonB():
  global c,now
  sql="SELECT id,Download,Upload FROM Log WHERE mdI='"+str(now[4])+"' AND adI='"+str(now[5])+"' AND (eseguita=0 OR eseguita=1 OR eseguita=2);"
  c.execute(sql)
  tot=0
  for row in c:
    tot=tot+row[1]+row[2]
  return tot

def startConnessione():
  global pid,comando
  checkLimit()
  lista=string.split(comando)
  execon=lista[0]; lista[0]=""
  args=""
  for a in range(len(lista)):
    args = args + " " + lista[a]
  pid=None
  if (checkConnessione()==False):
    writeAlert(interface + " not found. ",0)
    writeAlert("starting " + execon + " " + args,0)
    pid = subprocess.Popen([execon,args]).pid
    writeAlert("pid = " + str(pid),0)
    time.sleep(12)
    if (pid==None or checkConnessione()==False):
      exitError("Connection problem!")
  else:
    exitError("Connection " + str(interface) + " al ready exist.")

def stopConnessione():
  global pid,c,conn,interface
  if (c!=None):
      conn.commit()
      c.close()
      closeTuple()
  writeAlert("kill " + str(pid) + " ....",0)
  if (pid!=None):
    os.kill(pid,signal.SIGTERM)
    time.sleep(3)
    if (checkConnessione()):
      os.kill(pid,signal.SIGKILL)
      time.sleep(3)
  if (checkConnessione()):
    writeAlert(interface + " exist again? kill all ppp and wvdial...",0)
    os.system("pkill wvdial")
    time.sleep(2)
    os.system("pkill pppd")
    time.sleep(2)
    os.system("for a in `ps aux|grep wvdial|grep -v grep|gawk '{print $2}'`; do kill -s sigkill $a; done")
    time.sleep(2)
    os.system("for a in `ps aux|grep pppd|grep -v grep|gawk '{print $2}'`; do kill -s sigkill $a; done")
    time.sleep(2)
  if (checkConnessione()):
    writeAlert(interface + "exist AGAIN? test ping...",0)
    pingr=os.system('ping -I '+ str(interface) +' -c 1 www.google.com')
    if (pingr==0):
      writeAlert("Halt system!",0)
      time.sleep(60)
      os.system("halt")
  sysexit(0)    

def checkConnessione():    
  global interface
  if (os.path.exists("" + sysnet + interface)==True):
    writeAlert("check " + sysnet + interface + " ... Ok ",2)
    return True
  writeAlert("check " + sysnet + interface + " ... No.",0)
  return False



def getNow():
  a=time.localtime
  return [a()[3],a()[4],a()[5],a()[2],a()[1],a()[0]];
    

def startDb():
  global c,conn,db
  conn = sqlite3.connect(db)
  c = conn.cursor()
  c.execute("CREATE TABLE IF NOT EXISTS Log (id INTEGER, htI SMALLINT, mtI SMALLINT,stI SMALLINT, gdI SMALLINT, mdI SMALLINT,adI SMALLINT, htF SMALLINT, mtF SMALLINT,stF SMALLINT,gdF SMALLINT, mdF SMALLINT, adF SMALLINT, DownLoad BIGINT, UpLoad BIGINT, eseguita SMALLINT, PRIMARY KEY(id));");
  conn.commit()


def readRx():
  #forse ppp si sta avviando....
  if (os.access("" + sysnet + interface + "/statistics/rx_bytes",os.F_OK)==False):
    time.sleep(10)  
  if (os.access("" + sysnet + interface + "/statistics/rx_bytes",os.F_OK)==False):
    time.sleep(10)  
  if (os.access("" + sysnet + interface + "/statistics/rx_bytes",os.F_OK)==True):
    readfile = open("" + sysnet + interface + "/statistics/rx_bytes","r") 
    rx = readfile.readlines()
    readfile.close()
    return int(rx[0])
  else:
    exitError("rx_bytes read error.")

  
def readTx():
  if (os.access("" + sysnet + interface + "/statistics/tx_bytes",os.F_OK)==False):
    time.sleep(10)  
  if (os.access("" + sysnet + interface + "/statistics/tx_bytes",os.F_OK)==False):
    time.sleep(10)  
  if (os.access("" + sysnet + interface + "/statistics/tx_bytes",os.F_OK)==True):
    readfile = open("" + sysnet + interface + "/statistics/tx_bytes","r")
    tx = readfile.readlines()
    readfile.close()
    return int(tx[0])
  else:
    exitError("tx_bytes read error.")

    
def newTuple():
  global id,now,c
  id=idCon()
  now=getNow();
  sql="INSERT INTO Log (id,htI,mtI,stI,gdI,mdI,adI,htF,mtF,stF,gdF,mdF,adF,Download,Upload,eseguita) VALUES   ('"+str(id)+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','0','0','0');"
  c.execute(sql)
  conn.commit()


def idCon():
  global c
  c.execute("SELECT id FROM Log ORDER BY id;")
  r=list(c)
  if (len(r)==0):
    return 1
  else:
    last=r.pop()
    return int(last[0]) + 1 

    

def upTuple(rx,tx):
  global c,id
  c.execute("UPDATE Log SET Download='" + str(rx) + "',Upload='" + str(tx) + "' WHERE id='" + str(id) + "';")
  conn.commit()

  
def closeTuple():
  global c,id
  if (id!=None):
    print getNow()
    sql="UPDATE Log SET htF='" + str(getNow()[0]) + "',mtF='" + str(getNow()[1]) + "',stF='" + str(getNow()[2]) + "',gdF='" + str(getNow()[3]) + "',mdF='" + str(getNow()[4]) + "',adF='" + str(getNow()[5]) + "'  WHERE id='" + str(id) + "';"
    print sql
    c.execute(sql)
    conn.commit()


def exitError(mes):
  writeAlert(mes,0)
  stopConnessione()

def sysexit(o):
  global c
  if (os.access('/var/run/stopppy.pid',os.F_OK)==True):
    os.system('rm /var/run/stopppy.pid')
  if (c!=None):
    c.close();
  sys.exit(o)

def sigterm_handler(signum, frame):
  writeAlert( "Ah! ... SIGTERM!",0)
  stopConnessione()



#------------


signal.signal(signal.SIGTERM, sigterm_handler)                

#setup....
sysnet = "/sys/devices/virtual/net/"
c=None
conn=None;
i=None;
now=None
sesByteTx=sesByteRx=0
pid=None
id=None
bDayMax=None
bMonMax=None
bSinMax=None
bStop=None
interface=None
comando=None

fileconf="/etc/stopppy.conf"
db="/var/log/stopppy.db"

if (os.access(fileconf,os.F_OK)!=True):
  writeAlert(fileconf + " not found.")
  sysexit(1)

startDb()
now=getNow()

r = open(fileconf,"r")
dataconf = r.readlines()
r.close()
for row in dataconf:
  row=string.replace(row,"\n","")
  row=string.replace(row,"\t","")
  if (string.find(row,"#")==-1):
        if string.find(row,"MB-DAY-STOP")!=-1:   
          bDayMax=int(string.split(row).pop())*(1000000.0)
        if string.find(row,"MB-MON-STOP")!=-1:   
          bMonMax=int(string.split(row).pop())*(1000000.0)
        if string.find(row,"MB-SIN-STOP")!=-1:   
          bSinMax=int(string.split(row).pop())*(1000000.0)
        if string.find(row,"MB-PRE-STOP")!=-1:   
          bStop=int(string.split(row).pop())*(1000000.0)
        if string.find(row,"PPP-INTERFACE")!=-1: interface=string.split(row).pop()
        if string.find(row,"START-CONNECT")!=-1: comando=string.split(row).pop()
        

if (bDayMax==None and bMonMax==None and bSinMax==None):
  exitError("No set limits in stopppy.conf")
if (interface==None or comando==None):
  exitError("error in stopppy.conf")


if (len(sys.argv)>1):

 if (sys.argv[1]=="-help"):
    print "-update_bytes_day       bytes"
    print "-update_bytes_month     bytes"
    print "-statistics"
    print "-list"
    print "-start"
    print "-stop"
    sysexit()      

 if (sys.argv[1]=="-statistics"):
    checkConnessione()
    statistics()
    sysexit(0)  

 if (sys.argv[1]=="-list"):
    listConn()
    sysexit(0)  
    
 if (sys.argv[1]=="-stop"):
   if (os.access('/var/run/stopppy.pid',os.F_OK)==True):
     r = open('/var/run/stopppy.pid',"r")
     pidr = r.readlines()
     r.close()
     try:
       os.kill(int(pidr[0]),signal.SIGTERM)
     except:
       pass
     time.sleep(10)
     stopConnessione()
     sysexit(0) 
   else:
     writeAlert("/var/run/stoppy.pid error...",0)
     stopConnessione()
     sysexit(1)   

 if (sys.argv[1]=="-start"):
  #Daemonize....
  # Fork1
  if (os.access('/var/run/stopppy.pid',os.F_OK)==True):
    writeAlert("Stopppy al ready running....",0)
    sys.exit(1)
  try:
    if os.fork() > 0: os._exit(0) # ciao...
  except OSError, error:
    print 'fork #1 failed: %d (%s)' % (error.errno, error.strerror)
    os._exit(1)        
  os.chdir('/')
  os.setsid()
  os.umask(0)
  # Fork2
  try:
    pidf = os.fork()
    if pidf > 0:
      print 'Stopppy: running witch PID:' + str(pidf)
      pf = file("/var/run/stopppy.pid",'w')
      print >> pf,str(pidf)
      pf.close()
      os._exit(0)
  except OSError, error:
    print 'fork #2 failed: %d (%s)' % (error.errno, error.strerror)
    os._exit(1)
  
  #main:
 
  startConnessione()
  newTuple()
  while (True):    
  
      # Se ppp si disconette e riparte per i cavoli suoi
      if (sesByteTx>readTx() or sesByteRx>readRx()):
        writeAlert("Connection probabily lost...",0)
        newTuple()
      
      sesByteTx=readTx()
      sesByteRx=readRx()
      
      upTuple(sesByteRx,sesByteTx)
  
 #     statistics()
      checkLimit()
      time.sleep(15)
  
  
  closeTuple()
  conn.commit()
  c.close
  closeConnessione()
  sys.exit(0)


     
 if (len(sys.argv)>2):

    if (sys.argv[1]=="-update_bytes_day" and sys.argv[2]!=""):
      id=idCon()
      byte=int(sys.argv[2])
      tot=totDayB()
      diff=byte-tot
      sql="INSERT INTO Log (id,htI,mtI,stI,gdI,mdI,adI,htF,mtF,stF,gdF,mdF,adF,Download,Upload,eseguita) VALUES   ('"+str(id)+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','"+str(diff)+"','0','1');"
      c.execute(sql)
      conn.commit()
      statistics()
      sysexit(0)

    if (sys.argv[1]=="-update_bytes_month" and sys.argv[2]!=""):
      id=idCon()
      byte=int(sys.argv[2])
      tot=totMonB()
      diff=byte-tot
      sql="INSERT INTO Log (id,htI,mtI,stI,gdI,mdI,adI,htF,mtF,stF,gdF,mdF,adF,Download,Upload,eseguita) VALUES   ('"+str(id)+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','"+str(now[0])+"','"+str(now[1])+"','"+str(now[2])+"','"+str(now[3])+"','"+str(now[4])+"','"+str(now[5])+"','"+str(diff)+"','0','2');"
      c.execute(sql)
      conn.commit()
      statistics()
      sysexit(0)
      
  
 
 



